import {vec2} from 'gl-matrix';

export class Turtle {
  private gl: WebGLRenderingContext
  private program: WebGLProgram
  private theta: number
  private x: number
  private y: number
  private penStatus: 'up' | 'down'
  private points: vec2[][]
  private animationTimer: number | null

  constructor(gl: WebGLRenderingContext, program: WebGLProgram) {
    this.gl = gl
    this.program = program
    this.theta = 0
    this.x = 0
    this.y = 0
    this.penStatus = 'down'
    this.points = [[vec2.fromValues(0, 0)]]
    this.animationTimer = null
  }

  forward(distance: number) {
    this.x += distance * Math.cos((Math.PI * this.theta) / 180.0)
    this.y += distance * Math.sin((Math.PI * this.theta) / 180.0)
    if (this.penStatus === 'down') {
      const points = this.points
      points[points.length - 1].push(vec2.fromValues(this.x, this.y))
      this.render()
    }
  }

  right(angle: number) {
    this.theta += angle * -1
  }

  left(angle: number) {
    this.theta += angle
  }

  pen(action: 'up' | 'down') {
    this.penStatus = action
    if (action === 'up' && this.points[this.points.length - 1].length !== 0) {
      this.points.push([])
    } else if (action === 'down') {
      const segment = this.points[this.points.length - 1]
      segment[0] = vec2.fromValues(this.x, this.y)
    }
  }

  clear() {
    this.points = [[vec2.fromValues(0, 0)]]
    this.x = 0
    this.y = 0
    this.theta = 0
    this.gl.clear(this.gl.COLOR_BUFFER_BIT)
  }

  stop() {
    if (this.animationTimer) {
      clearTimeout(this.animationTimer)
      this.animationTimer = null
    }
  }

  animate(maxAngle: number, delay: number) {
    let i = 0
    const delayedIteration = () => {
      if (i < maxAngle) {
        this.theta = i
        for (let j = 0; j < 4; j++) {
          this.forward((1 / 360 / 5) * i)
          this.right(90)
        }
        this.animationTimer = window.setTimeout(() => {
          delayedIteration()
        }, delay)
        i += 5
      }
    }
    delayedIteration()
  }

  private render() {
    this.gl.clear(this.gl.COLOR_BUFFER_BIT)
    const vPosition = this.gl.getAttribLocation(this.program, 'a_Position')

    for (const pointSet of this.points) {
      const buffer = this.gl.createBuffer()
      this.gl.bindBuffer(this.gl.ARRAY_BUFFER, buffer)
      const flatPoints = new Float32Array(pointSet.flatMap(p => [p[0], p[1]]))
      this.gl.bufferData(this.gl.ARRAY_BUFFER, flatPoints, this.gl.STATIC_DRAW)
      this.gl.vertexAttribPointer(vPosition, 2, this.gl.FLOAT, false, 0, 0)
      this.gl.enableVertexAttribArray(vPosition)
      this.gl.drawArrays(this.gl.LINE_STRIP, 0, pointSet.length)
    }
  }
}
